﻿/* Pierwszy program Direct X w C# */


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
/* W tym miejscu dołączamy biblioteki */
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using D3D = Microsoft.DirectX.Direct3D;
using MyDX;




/*Tu dochodzi kilka nowych rzeczy */

namespace DirectX_Sample
{
    struct myownvertexformat
    {
        public Vector3 Pos;
        public Vector2 TexCoord;

        public myownvertexformat(Vector3 _Pos, float texx, float texy)
        {
            Pos = _Pos;
            TexCoord.X = texx;
            TexCoord.Y = texy;
        }
    }

    public class WinForm : System.Windows.Forms.Form        // Tworzymy klasę WinForm pochodną standardowej formy z WinAPI
    {
        private D3D.Device device;  //tu pierwsza nowość. Zmienna typu Device.

        private MyObject central, satellite1, satellite2, board, light;
        private System.ComponentModel.Container components = null;
        private Microsoft.DirectX.DirectInput.Device keyboard;

        private MyCamera Camera, Light, centralCamera;
       
    

        private D3D.Font Font3D;
        private float fps;
        private int frames=0, PrevTickCount=1;

        private D3D.Effect effect;

        private Matrix mProjection, mView;

        private RenderToSurface Rts, Rts2, SRts;

        private int MapSize = 512;


        private CubeTexture Kostka, ReflectionCube;
        private Matrix mProjectionCube, mViewCube;
        Surface RenderSurface;
        private Texture ScreenTexture, boardTexture;
        VertexBuffer mvb;
        VertexDeclaration mvd;

        private float BlurFactor=0.0f, FieldOfView=4.0f;


        public WinForm()  // konstruktor - inicjalizujemy komponenty formy (tak naprawdę to ich nie ma, ale wygodniej się pracuje w VS)
        {
            InitializeComponent();
            
        }

        private void UpdateFramerate()
        {
            frames++;
            if (Math.Abs(Environment.TickCount - PrevTickCount) > 1000)
            {
                fps = (float)frames * 1000 / Math.Abs(Environment.TickCount - PrevTickCount);
                PrevTickCount = Environment.TickCount;
                frames = 0;
            }
            
        }

        public void InitializeKeyboard()
        {
            keyboard = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
            keyboard.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            keyboard.Acquire();
        }

        
        public void InitializeMeshes()
        {

            board = new MyObject(ref device);
            board.CreateQuadrangle(10, 10);
            //board.Normalize(Matrix.Scaling(0.5f, 0.5f, 0.5f));
            board.LoadTexture(0, "C:\\x\\Cedfence.bmp");
            board.Position = new Vector3(0.0f, 0.0f, 0.0f);
            board.Rotation = Quaternion.Identity;

            central = new MyObject(ref device);
            central.LoadFromFile("c:\\x\\kula.x");
            central.Normalize(Matrix.Scaling(1f, 1f, 1f));
            central.Position = new Vector3(0.0f, 1.0f, 0.0f);
            central.Rotation = Quaternion.Identity;

            satellite1 = new MyObject(ref device);
            satellite1.LoadFromFile("c:\\x\\kulka2.x");
            satellite1.Normalize(Matrix.Scaling(1f, 1f, 1f));
            satellite1.Position = new Vector3(3.0f, 1.0f, 0.0f);
            satellite1.Rotation = Quaternion.Identity;

            satellite2 = new MyObject(ref device);
            satellite2.LoadFromFile("c:\\x\\kulka.x");
            satellite2.Normalize(Matrix.Scaling(1f, 1f, 1f));
            satellite2.Position = new Vector3(2.0f, 1.0f, 3.0f);
            satellite2.Rotation = Quaternion.Identity;

            light = new MyObject(ref device);
            light.LoadFromFile("c:\\x\\room\\light.x");
            light.Normalize(Matrix.Scaling(0.1f,0.1f,0.1f));
            light.Rotation = Quaternion.Identity;
          
            Camera = new MyCamera();
            Light = new MyCamera();
            Camera.Position = new Vector3(7f, 2f, -7f);
            Camera.Up = new Vector3(0, 1.0f,0);
            Light.Position = new Vector3(0, 3f, 0.0f);
            centralCamera = new MyCamera();
            centralCamera.Position = new Vector3(0.0f, 0.5f, 0.0f);
            centralCamera.Up = new Vector3(0, 1.0f, 0);

            Kostka = new CubeTexture(device, MapSize, 1, Usage.RenderTarget, Format.R32F, Pool.Default);

            ReflectionCube = new CubeTexture(device, MapSize, 1, Usage.RenderTarget, Format.R32F, Pool.Default);
            SRts = new RenderToSurface(device, this.Width, this.Height, Format.X8R8G8B8, true, DepthFormat.D16);
            ScreenTexture = new Texture(device, this.Width, this.Height, 1, Usage.RenderTarget, Format.X8R8G8B8, Pool.Default);
            PrepareScreenStrip();

        }

        public void PrepareScreenStrip()
        {
            mvb = new VertexBuffer(typeof(myownvertexformat), 4, device, Usage.WriteOnly, VertexFormats.Position | VertexFormats.Texture0, Pool.Managed);

            myownvertexformat[] vertices = new myownvertexformat[4];

            vertices[0] = new myownvertexformat(new Vector3(-1, 1, 0), 0.0f, 0.0f);
            vertices[1] = new myownvertexformat(new Vector3(1, 1, 0), 1.0f, 0.0f);
            vertices[2] = new myownvertexformat(new Vector3(-1, -1, 0), 0.0f, 1.0f);
            vertices[3] = new myownvertexformat(new Vector3(1, -1, 0), 1.0f, 1.0f);

            mvb.SetData(vertices, 0, LockFlags.None);

            VertexElement[] velements = new VertexElement[]
             {
                 new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
                 new VertexElement(0, 12, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
                 VertexElement.VertexDeclarationEnd
             };
            mvd = new VertexDeclaration(device, velements);

        }

        public void RenderScreen()
        {


            effect.Technique = "T2S";

            device.SetStreamSource(0, mvb, 0);
            device.VertexDeclaration = mvd;


            int numpasses = effect.Begin(0);
            for (int i = 0; i < numpasses; i++)
            {
                effect.BeginPass(i);

                device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);

                effect.EndPass();
            }
            effect.End();

        }

        public void UpdateCamera()
        {

            float aspect_ratio = this.Width / (float)this.Height;
            mProjection = Matrix.PerspectiveFovLH((float)Math.PI / 4, aspect_ratio, 0.02f, 15);  // macierz projekcji
            mView = Camera.ViewMatrix(new Vector3(0.0f, 0.0f, 0.0f));
            
        }

        private void RenderScene()
        {


            RenderSurface = ScreenTexture.GetSurfaceLevel(0);
            SRts.BeginScene(RenderSurface);
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);

            effect.Technique = "nolighttextured";
            RenderObject(light, mView, mProjection);
            effect.SetValue("xReflectionIntensity", 1.0f);

            effect.Technique = "reflected";
            RenderObject(central, mView, mProjection);


            effect.Technique = "textured";
            RenderObject(satellite1, mView, mProjection);
            RenderObject(satellite2, mView, mProjection);

            effect.Technique = "textured";
            RenderObject(board, mView, mProjection);

            

            SRts.EndScene(Filter.None);
        }


        public void RenderObject(MyObject O,Matrix View, Matrix Projection)
        {
            effect.SetValue("xRotationMatrix", Matrix.RotationQuaternion(O.Rotation));
            device.SetStreamSource(0, O.vb, 0);
            device.VertexDeclaration = O.vd;
            effect.SetValue("xWorld", Matrix.RotationQuaternion(O.Rotation)*Matrix.Translation(O.Position));
            effect.SetValue("xView", View);
            effect.SetValue("xProjection", Projection);

            int passes = effect.Begin(0);
            for (int i = 0; i < passes; i++)
            {
                effect.BeginPass(i);
                O.Render(ref effect, "xTexture");
                effect.EndPass();
            }
            effect.End();
        }

        private void RenderReflection(int i)
        {

            switch (i)
            {
                case 0:
                    centralCamera.Position = new Vector3(0f, 0f, -central.radius - 0.1f);
                    centralCamera.Up = new Vector3(0, 1.0f, 0);
                    mView = centralCamera.ViewMatrix(new Vector3(0f, 0f, -23.5f * 0.5f));
                    
                    RenderSurface = ReflectionCube.GetCubeMapSurface(CubeMapFace.NegativeZ, 0);
                    break;

                case 1:
                    centralCamera.Position = new Vector3(-central.radius - 0.1f, 0f, 0);
                    centralCamera.Up = new Vector3(0, 1.0f, 0);
                    mView = centralCamera.ViewMatrix(new Vector3(-23.5f * 0.5f, 0f, 0f));
                    RenderSurface = ReflectionCube.GetCubeMapSurface(CubeMapFace.NegativeX, 0);
                    break;


                case 2:
                    centralCamera.Position = new Vector3(0f, 0f, central.radius + 0.1f);
                    centralCamera.Up = new Vector3(0, 1.0f, 0);
                    mView = centralCamera.ViewMatrix(new Vector3(0f, 0f, 23.5f * 0.5f));
                    RenderSurface = ReflectionCube.GetCubeMapSurface(CubeMapFace.PositiveZ, 0);
                    break;

                case 3:
                    centralCamera.Position = new Vector3(central.radius + 0.1f, 0f, 0);
                    centralCamera.Up = new Vector3(0, 1.0f, 0);
                    mView = centralCamera.ViewMatrix(new Vector3(23.5f * 0.5f, 0f, 0f));
                    RenderSurface = ReflectionCube.GetCubeMapSurface(CubeMapFace.PositiveX, 0);
                    break;

                case 4:
                    centralCamera.Position = new Vector3(0, -1.3f - 0.1f, 0);
                    centralCamera.Up = new Vector3(0, 0, -1.0f);
                    mView = centralCamera.ViewMatrix(new Vector3(0f, -13.5f * 0.5f, 0f));
                    RenderSurface = ReflectionCube.GetCubeMapSurface(CubeMapFace.NegativeY, 0);
                    break;

                case 5:
                    centralCamera.Position = new Vector3(0, 1.3f + 0.1f, 0);
                    centralCamera.Up = new Vector3(0, 0, -1.0f);
                    mView = centralCamera.ViewMatrix(new Vector3(0f, 13.5f * 0.5f, 0f));
                    RenderSurface = ReflectionCube.GetCubeMapSurface(CubeMapFace.PositiveY, 0);
                    break;

            }
            effect.SetValue("xCameraPosition", new Vector4(centralCamera.Position.X, centralCamera.Position.Y, centralCamera.Position.Z, 1.0f));
            effect.Technique = "textured";

            Rts.BeginScene(RenderSurface);

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);

            RenderObject(central, mView, mProjection);
            RenderObject(light, mView, mProjection);
            RenderObject(satellite1, mView, mProjection);
            RenderObject(satellite2, mView, mProjection);

            Rts.EndScene(Filter.None);
        }


        public void DepthRender()
        {

            device.RenderState.CullMode = Cull.None;

            effect.Technique = "cubedepth"; //!!!! Technika na depth
            
            effect.SetValue("xLightPosition", new Vector4(Light.Position.X, Light.Position.Y, Light.Position.Z, 1.0f));
            mProjectionCube = Matrix.PerspectiveFovLH((float)Math.PI / 2.0f, 1.0f, 0.02f, 15);  // macierz projekcji swiatla

            RenderSurface = Kostka.GetCubeMapSurface(CubeMapFace.NegativeX, 0);
            mViewCube = Matrix.LookAtLH(Light.Position, Light.Position + new Vector3(-1, 0, 0), new Vector3(0, 1, 0));
            Rts.BeginScene(RenderSurface);
             device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
             RenderObject(board, mViewCube, mProjectionCube);
             RenderObject(central, mViewCube, mProjectionCube);
             RenderObject(satellite1, mViewCube, mProjectionCube);
             RenderObject(satellite2, mViewCube, mProjectionCube);
            Rts.EndScene(Filter.None);

            mViewCube = Matrix.LookAtLH(Light.Position, Light.Position + new Vector3(1, 0, 0), new Vector3(0, 1, 0));
            RenderSurface = Kostka.GetCubeMapSurface(CubeMapFace.PositiveX, 0);
            Rts.BeginScene(RenderSurface);
             device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
             RenderObject(board, mViewCube, mProjectionCube);
             RenderObject(central, mViewCube, mProjectionCube);
             RenderObject(satellite1, mViewCube, mProjectionCube);
             RenderObject(satellite2, mViewCube, mProjectionCube);
            Rts.EndScene(Filter.None);

            mViewCube = Matrix.LookAtLH(Light.Position, Light.Position + new Vector3(0, 1, 0), new Vector3(0, 0, -1));
            RenderSurface = Kostka.GetCubeMapSurface(CubeMapFace.PositiveY, 0);
            Rts.BeginScene(RenderSurface);
             device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
             RenderObject(board, mViewCube, mProjectionCube);
             RenderObject(central, mViewCube, mProjectionCube);
             RenderObject(satellite1, mViewCube, mProjectionCube);
             RenderObject(satellite2, mViewCube, mProjectionCube);
            Rts.EndScene(Filter.None);

            mViewCube = Matrix.LookAtLH(Light.Position, Light.Position + new Vector3(0, -1, 0), new Vector3(0, 0, 1));
            RenderSurface = Kostka.GetCubeMapSurface(CubeMapFace.NegativeY, 0);
            Rts.BeginScene(RenderSurface);
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
            RenderObject(board, mViewCube, mProjectionCube);
            RenderObject(central, mViewCube, mProjectionCube);
            RenderObject(satellite1, mViewCube, mProjectionCube);
            RenderObject(satellite2, mViewCube, mProjectionCube);
            Rts.EndScene(Filter.None);

            mViewCube = Matrix.LookAtLH(Light.Position, Light.Position + new Vector3(0, 0, 1), new Vector3(0, 1, 0));
            RenderSurface = Kostka.GetCubeMapSurface(CubeMapFace.PositiveZ, 0);
            Rts.BeginScene(RenderSurface);
             device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
             RenderObject(board, mViewCube, mProjectionCube);
             RenderObject(central, mViewCube, mProjectionCube);
             RenderObject(satellite1, mViewCube, mProjectionCube);
             RenderObject(satellite2, mViewCube, mProjectionCube);
            Rts.EndScene(Filter.None);

            mViewCube = Matrix.LookAtLH(Light.Position, Light.Position + new Vector3(0, 0, -1), new Vector3(0, 1, 0));
            RenderSurface = Kostka.GetCubeMapSurface(CubeMapFace.NegativeZ, 0);
            Rts.BeginScene(RenderSurface);
             device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
             RenderObject(board, mViewCube, mProjectionCube);
             RenderObject(central, mViewCube, mProjectionCube);
             RenderObject(satellite1, mViewCube, mProjectionCube);
             RenderObject(satellite2, mViewCube, mProjectionCube);
            Rts.EndScene(Filter.None);

            device.RenderState.CullMode = Cull.CounterClockwise;
        }

        public void InitializeDevice() // Druga nowość. Inicjujemy urządzenie.
        {
            
            PresentParameters presentParams = new PresentParameters();
            presentParams.BackBufferWidth = 1024;
            presentParams.BackBufferHeight = 768;
            presentParams.BackBufferFormat = Format.A8R8G8B8;
            presentParams.BackBufferCount = 1;

            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            device = new D3D.Device(0, D3D.DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, presentParams);
  
            System.Drawing.Font Font2D = new System.Drawing.Font("Arial", 12f, FontStyle.Regular);
            Font3D = new D3D.Font(device, Font2D);

            
           // effect = D3D.Effect.FromFile(device, "c:\\x\\room\\effect.fx", null, ShaderFlags.None, null);
            effect = D3D.Effect.FromFile(device, "D:\\VCProjects\\grafika3d\\room\\effect.fx", null, ShaderFlags.None, null);
            Rts = new RenderToSurface(device, MapSize, MapSize, Format.R32F, true, DepthFormat.D16);
            Rts2 = new RenderToSurface(device, MapSize, MapSize, Format.X8R8G8B8, true, DepthFormat.D16);

        }



        private void ReadKeyboard()
        {
            KeyboardState keys = keyboard.GetCurrentKeyboardState();
            float step = 0.1f;
            bool l = false;
            Vector3 V=Camera.Position;
            if (keys[Key.LeftShift]) step = 0.05f;
            if (keys[Key.LeftControl]) { V = Light.Position; l = true; }
            if (keys[Key.Right]) V.X += step;
            if (keys[Key.Left]) V.X -= step;
            if (keys[Key.Up]) V.Y += step;
            if (keys[Key.Down]) V.Y -= step;
            if (keys[Key.A]) V.Z += step;
            if (keys[Key.Z]) V.Z -= step;
            if (keys[Key.LeftBracket]) FieldOfView -= 0.1f;
            if (keys[Key.RightBracket]) FieldOfView += 0.1f;
            if (keys[Key.Comma]) BlurFactor -= 0.01f;
            if (keys[Key.Period]) BlurFactor += 0.01f;
            if (l) Light.Position = V;
            else Camera.Position = V;
            
        }

        public void UpdateLights()
        {
            light.Position = Light.Position;
           
        }
             
        


        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) //Trzecia nowość. W zdarzeniu OnPaint będziemy rysować. 
        {

            //for (int i = 0; i < 6; i++)
            //{
            //    RenderReflection(i);
            //}  
              ReadKeyboard();
              UpdateLights();
              UpdateCamera();
              UpdateFramerate();

              
              device.RenderState.ColorWriteEnable = ColorWriteEnable.Red;
              DepthRender();

              device.RenderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha;


              effect.SetValue("xCubeShadowMap", Kostka);
              effect.SetValue("xLightPosition", new Vector4(Light.Position.X, Light.Position.Y, Light.Position.Z, 1.0f));
              effect.SetValue("xCameraPosition", new Vector4(Camera.Position.X, Camera.Position.Y, Camera.Position.Z, 1.0f));
              effect.SetValue("xAmbientLight", new Vector4(0.1f, 0.1f, 0.1f, 0.1f));
              effect.SetValue("xReflectionCube", ReflectionCube);
              effect.SetValue("xReflectionIntensity", 2.9f);
              effect.SetValue("xSpecularFactor", 0.9f);
              effect.SetValue("xSpecularPower", 50f);
              effect.SetValue("xSpecularColor", new Vector4(1f, 1f, 1f, 0f));

              RenderScene();
              effect.SetValue("xTexture", ScreenTexture);
              device.BeginScene(); // początek sceny 
              device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.PowderBlue, 1.0f, 0);

              RenderScreen();

              Font3D.DrawText(null, string.Format("{0:0.00} FPS", fps), new Point(10, 10), Color.White);
              
             
              using (Sprite spriteobject = new Sprite(device))
              {
                  spriteobject.Begin(SpriteFlags.DoNotSaveState);
                  spriteobject.Transform = Matrix.Scaling(0.2f, 0.2f, 0.2f);
                 // spriteobject.Draw(BlurMap, new Rectangle(0, 0, MapSize, MapSize), new Vector3(0, 0, 0), new Vector3(0, 0, 0f), Color.White);
                  spriteobject.End();
              }
              
              device.EndScene();  //koniec sceny
            
              device.Present();

              satellite1.RotateY(0.1f);

              this.Invalidate(); //to po to aby windows przerysował formę


        }

        protected override void Dispose(bool disposing) // tu zwalniamy wszystkie komponenty
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }
        private void InitializeComponent() // tu metoda inicjująca komponenty
        {
            this.components = new System.ComponentModel.Container();
            
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.Opaque, true);
            this.Size = new System.Drawing.Size(1024, 768);
            //this.FormBorderStyle = FormBorderStyle.None;

            this.Text = "ttt";
            

        }

        static void Main()  // tu uruchamiamy aplikację.
        {
            using (WinForm dx_form = new WinForm())
            {
                dx_form.InitializeKeyboard();
                dx_form.InitializeDevice(); // Czwarta nowość. Trzeba zainicjować Device
                dx_form.InitializeMeshes();
                Application.Run(dx_form);
            }
        }

       
    }
}
 